﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Threading;
using System.Security.Principal;
using System.Security.AccessControl;

using AMPLibrary.Collections;
using AMPLibrary.Media;
using AMPLibrary.Windows;

namespace AMPLibrary.Controllers
{
    public class WatchFolderController : IDisposable
    {
        private static WatchFolderController watchFolderController;

        Collection<string> musicMediaCollection = new Collection<string>();

        Collection<string> videoMediaCollection = new Collection<string>();

        Collection<Playlist> playlistMediaCollection = new Collection<Playlist>();

        private Thread musicWatchThread;

        private Thread videoWatchThread;

        private Thread playlistWatchThread;

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        public delegate void MusicScanCompleteEventHandler(object sender, EventArgs e);

        public delegate void VideoScanCompleteEventHandler(object sender, EventArgs e);

        public delegate void PlaylistScanCompleteEventHandler(object sender, EventArgs e);

        public event PlaylistScanCompleteEventHandler PlaylistScanComplete;

        public event VideoScanCompleteEventHandler VideoScanComplete;

        public event MusicScanCompleteEventHandler MusicScanComplete;

        private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;

        string _currentMusicFolder = string.Empty;

        string _currentVideoFolder = string.Empty;

        string _currentPlaylistFolder = string.Empty;

        /// <summary>
        /// 
        /// </summary>
        public string CurrentMusicFolder
        {
            get
            {
                return _currentMusicFolder;
            }
            private set
            {
                _currentMusicFolder = value;
                OnPropertyChanged("CurrentMusicFolder");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string CurrentVideoFolder
        {
            get
            {
                return _currentVideoFolder;
            }
            private set
            {
                _currentVideoFolder = value;
                OnPropertyChanged("CurrentVideoFolder");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string CurrentPlaylistFolder
        {
            get
            {
                return _currentPlaylistFolder;
            }
            private set
            {
                _currentPlaylistFolder = value;
                OnPropertyChanged("CurrentPlaylistFolder");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool PauseMusic
        {
            get;
            set;
        }

        /// <summary>
        /// 
        /// </summary>
        public bool PauseVideo
        {
            get;
            set;
        }

        /// <summary>
        /// 
        /// </summary>
        public bool PausePlaylist
        {
            get;
            set;
        }

        /// <summary>
        /// 
        /// </summary>
        private WatchFolderController()
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        protected void OnPropertyChanged(string propertyName)
        {
            if (dispatcher.Thread == Thread.CurrentThread)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            else
                dispatcher.BeginInvoke(DispatcherPriority.DataBind, (ThreadStart)delegate() { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); });
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void _MusicScanComplete(EventArgs e)
        {
            if (MusicScanComplete != null)
            {
                if (dispatcher.Thread == Thread.CurrentThread)
                    MusicScanComplete(this, e);
                else
                    dispatcher.BeginInvoke(DispatcherPriority.DataBind, (ThreadStart)delegate() { MusicScanComplete(this, e); });
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void _VideoScanComplete(EventArgs e)
        {
            if (VideoScanComplete != null)
            {
                if (dispatcher.Thread == Thread.CurrentThread)
                    VideoScanComplete(this, e);
                else
                    dispatcher.BeginInvoke(DispatcherPriority.DataBind, (ThreadStart)delegate() { VideoScanComplete(this, e); });
            }
        }
        
        /// <summary>
        ///
        /// </summary>
        /// <param name="e"></param>
        protected virtual void _PlaylistScanComplete(EventArgs e)
        {
            if (PlaylistScanComplete != null)
            {
                if (dispatcher.Thread == Thread.CurrentThread)
                    PlaylistScanComplete(this, e);
                else
                    dispatcher.BeginInvoke(DispatcherPriority.DataBind, (ThreadStart)delegate() { PlaylistScanComplete(this, e); });
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            Reset();
        }

        /// <summary>
        /// 
        /// </summary>
        private void Reset()
        {
            if (musicWatchThread != null)
            {
                musicWatchThread.Abort();

                while (musicWatchThread.ThreadState != ThreadState.Aborted)
                    ;

                musicWatchThread = null;
            }

            if (videoWatchThread != null)
            {
                videoWatchThread.Abort();

                while (videoWatchThread.ThreadState != ThreadState.Aborted)
                    ;

                videoWatchThread = null;
            }

            if (playlistWatchThread != null)
            {
                playlistWatchThread.Abort();

                while (playlistWatchThread.ThreadState != ThreadState.Aborted)
                    ;

                playlistWatchThread = null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static WatchFolderController GetWatchFolderController()
        {
            if (watchFolderController == null)
            {
                watchFolderController = new WatchFolderController();
                watchFolderController.PauseMusic = false;
                watchFolderController.PauseVideo = false;
                watchFolderController.PausePlaylist = false;
            }

            return watchFolderController;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Start(WatchFolderScanModes watchFolderScanMode)
        {
            switch (watchFolderScanMode)
            {
                case WatchFolderScanModes.DoNotScan:
                    break;

                case WatchFolderScanModes.FullComputerScan:
                    Reset();

                    musicWatchThread = new Thread(delegate() { WatchComputerMusic(); });
                    musicWatchThread.Priority = ThreadPriority.BelowNormal;

                    if (musicWatchThread.ThreadState != ThreadState.Running)
                        musicWatchThread.Start();

                    videoWatchThread = new Thread(delegate() { WatchComputerVideo(); });
                    videoWatchThread.Priority = ThreadPriority.BelowNormal;

                    if (videoWatchThread.ThreadState != ThreadState.Running)
                        videoWatchThread.Start();

                    playlistWatchThread = new Thread(delegate() { WatchComputerPlaylist(); });
                    playlistWatchThread.Priority = ThreadPriority.BelowNormal;

                    if (playlistWatchThread.ThreadState != ThreadState.Running)
                        playlistWatchThread.Start();
                    break;

                case WatchFolderScanModes.WatchFolderScan:
                    Reset();

                    musicWatchThread = new Thread(delegate() { WatchFolderMusic(); });
                    musicWatchThread.Priority = ThreadPriority.BelowNormal;

                    if (musicWatchThread.ThreadState != ThreadState.Running)
                        musicWatchThread.Start();

                    videoWatchThread = new Thread(delegate() { WatchFolderVideo(); });
                    videoWatchThread.Priority = ThreadPriority.BelowNormal;

                    if (videoWatchThread.ThreadState != ThreadState.Running)
                        videoWatchThread.Start();

                    playlistWatchThread = new Thread(delegate() { WatchFolderPlaylist(); });
                    playlistWatchThread.Priority = ThreadPriority.BelowNormal;

                    if (playlistWatchThread.ThreadState != ThreadState.Running)
                        playlistWatchThread.Start();
                    break;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Stop()
        {
            Reset();

            this.CurrentMusicFolder = this.CurrentVideoFolder = this.CurrentPlaylistFolder = "Scanning stopped";
        }

        /// <summary>
        /// 
        /// </summary>
        private void WatchComputerMusic()
        {
            MusicCollection mc = MusicCollection.GetMusicCollection();
            Collection<MediaFile> mediaToRemove = new Collection<MediaFile>();

            while (true)
            {
                try
                {
                    musicMediaCollection.ClearItems();

                    foreach (DriveInfo driveInfo in DriveInfo.GetDrives())
                    {
                        if(driveInfo.DriveType == DriveType.Removable || driveInfo.DriveType == DriveType.CDRom || driveInfo.DriveType == DriveType.Ram || driveInfo.DriveType == DriveType.NoRootDirectory || driveInfo.DriveType == DriveType.Unknown)
                            continue;

                        string path = driveInfo.Name;

                        // scan the first music folder
                        ScanMusicFolder(path);

                        this.CurrentMusicFolder = "Currently adding files to the library.";

                        foreach (string mediaPath in musicMediaCollection)
                        {
                            while (this.PauseMusic)
                                ;

                            var isInCollection = (from media in mc where media.Path == mediaPath select media);

                            if (isInCollection.Count() == 0 && ExcludedMediaCollection.GetExcludedMediaCollection().IsExcluded(mediaPath) == false)
                                mc.AddItem(MediaFile.LoadMedia(mediaPath, DateTime.Now));
                        }

                        musicMediaCollection.ClearItems();

                        _MusicScanComplete(EventArgs.Empty);
                    }

                    // remove any media that no longer exists
                    foreach (MediaFile item in mc)
                    {
                        if (!File.Exists(item.Path))
                            mediaToRemove.AddItem(item);
                    }

                    foreach (MediaFile item in mediaToRemove)
                        mc.RemoveItem(item);

                    _MusicScanComplete(EventArgs.Empty);

                    this.CurrentMusicFolder = "Scan Complete! Next Scan " + DateTime.Now.AddMinutes(Settings.WatchFolderScanInterval).ToLongTimeString();

                    // wait for next scan
                    Thread.Sleep(Settings.WatchFolderScanInterval * 60000);
                }
                catch
                {
                }
            }
        }

        private void WatchComputerVideo()
        {
            VideoCollection vc = VideoCollection.GetVideoCollection();
            Collection<MediaFile> mediaToRemove = new Collection<MediaFile>();

            while (true)
            {
                try
                {
                    videoMediaCollection.ClearItems();

                    foreach (DriveInfo driveInfo in DriveInfo.GetDrives())
                    {
                        if (driveInfo.DriveType == DriveType.Removable || driveInfo.DriveType == DriveType.CDRom || driveInfo.DriveType == DriveType.Ram || driveInfo.DriveType == DriveType.NoRootDirectory || driveInfo.DriveType == DriveType.Unknown)
                            continue;

                        string path = driveInfo.Name;

                        // scan the first music folder
                        ScanVideoFolder(path);

                        this.CurrentVideoFolder = "Currently adding files to the library.";

                        foreach (string mediaPath in videoMediaCollection)
                        {
                            while (this.PauseVideo)
                                ;

                            var isInCollection = (from media in vc where media.Path == mediaPath select media);

                            if (isInCollection.Count() == 0 && ExcludedMediaCollection.GetExcludedMediaCollection().IsExcluded(mediaPath) == false)
                                vc.AddItem(MediaFile.LoadMedia(mediaPath, DateTime.Now));
                        }

                        videoMediaCollection.ClearItems();

                        _VideoScanComplete(EventArgs.Empty);
                    }

                    // remove any media that no longer exists
                    foreach (MediaFile item in vc)
                    {
                        if (!File.Exists(item.Path))
                            mediaToRemove.AddItem(item);
                    }

                    foreach (MediaFile item in mediaToRemove)
                        vc.RemoveItem(item);

                    this.CurrentVideoFolder = "Scan Complete! Next Scan " + DateTime.Now.AddMinutes(Settings.WatchFolderScanInterval).ToLongTimeString();

                    _VideoScanComplete(EventArgs.Empty);

                    // wait for next scan
                    Thread.Sleep(Settings.WatchFolderScanInterval * 60000);
                }
                catch
                {
                }
            }
        }

        private void WatchComputerPlaylist()
        {
            PlaylistCollection pc = PlaylistCollection.GetPlaylistCollection();
            Collection<Playlist> playlistToRemove = new Collection<Playlist>();

            while (true)
            {
                try
                {
                    playlistMediaCollection.ClearItems();


                    foreach (DriveInfo driveInfo in DriveInfo.GetDrives())
                    {
                        if (driveInfo.DriveType == DriveType.Removable || driveInfo.DriveType == DriveType.CDRom || driveInfo.DriveType == DriveType.Ram || driveInfo.DriveType == DriveType.NoRootDirectory || driveInfo.DriveType == DriveType.Unknown)
                            continue;

                        string path = driveInfo.Name;

                        // scan the first music folder
                        ScanPlaylistFolder(path);

                        this.CurrentPlaylistFolder = "Currently adding files to the library.";

                        foreach (Playlist item in playlistMediaCollection)
                        {
                            while (this.PausePlaylist)
                                ;

                            var isInCollection = (from media in pc where media.Path == item.Path select media);

                            if (isInCollection.Count() == 0 && ExcludedMediaCollection.GetExcludedMediaCollection().IsExcluded(item.Path) == false)
                                pc.AddItem(item);
                        }

                        playlistMediaCollection.ClearItems();

                        _PlaylistScanComplete(EventArgs.Empty);
                    }

                    // remove any media that no longer exists
                    foreach (Playlist item in pc)
                    {
                        if (!File.Exists(item.Path))
                            playlistToRemove.AddItem(item);
                    }

                    foreach (Playlist item in playlistToRemove)
                        pc.RemoveItem(item);

                    _PlaylistScanComplete(EventArgs.Empty);

                    this.CurrentPlaylistFolder = "Scan Complete! Next Scan " + DateTime.Now.AddMinutes(Settings.WatchFolderScanInterval).ToLongTimeString();

                    // wait for next scan
                    Thread.Sleep(Settings.WatchFolderScanInterval * 60000);
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void WatchFolderMusic()
        {
            MusicCollection mc = MusicCollection.GetMusicCollection();
            Collection<MediaFile> mediaToRemove = new Collection<MediaFile>();

            while (true)
            {
                try
                {
                    musicMediaCollection.ClearItems();

                    foreach (string path in (SplittedArrayList)Settings.MusicWatchFolders)
                    {
                        // scan the first music folder
                        ScanMusicFolder(path);

                        this.CurrentMusicFolder = "Currently adding files to the library.";
                    
                        foreach (string mediaPath in musicMediaCollection)
                        {
                            while (this.PauseMusic)
                                ;

                            var isInCollection = (from media in mc where media.Path == mediaPath select media);

                            if (isInCollection.Count() == 0 && ExcludedMediaCollection.GetExcludedMediaCollection().IsExcluded(mediaPath) == false)
                                mc.AddItem(MediaFile.LoadMedia(mediaPath, DateTime.Now));
                        }

                        musicMediaCollection.ClearItems();

                        _MusicScanComplete(EventArgs.Empty);
                    }

                    // remove any media that no longer exists
                    foreach (MediaFile item in mc)
                    {
                        if (!File.Exists(item.Path))
                            mediaToRemove.AddItem(item);
                    }

                    foreach (MediaFile item in mediaToRemove)
                        mc.RemoveItem(item);

                    _MusicScanComplete(EventArgs.Empty);

                    this.CurrentMusicFolder = "Scan Complete! Next Scan " + DateTime.Now.AddMinutes(Settings.WatchFolderScanInterval).ToLongTimeString();
                    
                    // wait for next scan
                    Thread.Sleep(Settings.WatchFolderScanInterval * 60000);
                }
                catch
                {
                }
            }
        }
        
        /// <summary>
        /// Method, ScanMusicFolder
        /// 
        /// Recursively scans a path looking for valid music media types
        /// if an item is found, it is added to the music media collection whose items will be added to the main collection
        /// </summary>
        /// <param name="path"></param>
        private void ScanMusicFolder(string path)
        {
            if (FileSystemAccess.IsRestricted(path))
                return;

            this.CurrentMusicFolder = path;

            try
            {
                foreach (string directoryPath in Directory.GetDirectories(path))
                {
                    try
                    {
                        foreach (string filePath in Directory.GetFiles(directoryPath))
                        {
                            if (MediaTypes.IsValidMusicMediaType(filePath))
                                musicMediaCollection.AddItem(filePath);
                        }
                    }
                    catch
                    {
                        // could not add file
                    }

                    ScanMusicFolder(directoryPath);
                }

                try
                {
                    foreach (string filePath in Directory.GetFiles(path))
                    {
                        if (MediaTypes.IsValidMusicMediaType(filePath))
                            musicMediaCollection.AddItem(filePath);
                    }
                }
                catch
                {
                    // could not add file
                }
            }
            catch
            {
                // could not find path
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void WatchFolderVideo()
        {
            VideoCollection vc = VideoCollection.GetVideoCollection();
            Collection<MediaFile> mediaToRemove = new Collection<MediaFile>();

            while (true)
            {
                try
                {
                    videoMediaCollection.ClearItems();

                    foreach (string path in (SplittedArrayList)Settings.VideoWatchFolders)
                    {
                        // scan the first music folder
                        ScanVideoFolder(path);

                        this.CurrentVideoFolder = "Currently adding files to the library.";

                        foreach (string mediaPath in videoMediaCollection)
                        {
                            while (this.PauseVideo)
                                ;

                            var isInCollection = (from media in vc where media.Path == mediaPath select media);

                            if (isInCollection.Count() == 0 && ExcludedMediaCollection.GetExcludedMediaCollection().IsExcluded(mediaPath) == false)
                                vc.AddItem(MediaFile.LoadMedia(mediaPath, DateTime.Now));
                        }

                        videoMediaCollection.ClearItems();

                        _VideoScanComplete(EventArgs.Empty);
                    }

                    // remove any media that no longer exists
                    foreach (MediaFile item in vc)
                    {
                        if (!File.Exists(item.Path))
                            mediaToRemove.AddItem(item);
                    }

                    foreach (MediaFile item in mediaToRemove)
                        vc.RemoveItem(item);

                    this.CurrentVideoFolder = "Scan Complete! Next Scan " + DateTime.Now.AddMinutes(Settings.WatchFolderScanInterval).ToLongTimeString();

                    _VideoScanComplete(EventArgs.Empty);
                    
                    // wait for next scan
                    Thread.Sleep(Settings.WatchFolderScanInterval * 60000);
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        private void ScanVideoFolder(string path)
        {
            if (FileSystemAccess.IsRestricted(path))
                return;

            this.CurrentVideoFolder = path;

            try
            {
                foreach (string directoryPath in Directory.GetDirectories(path))
                {
                    try
                    {
                        foreach (string filePath in Directory.GetFiles(directoryPath))
                        {
                            if (MediaTypes.IsValidVideoMediaType(filePath))
                                videoMediaCollection.AddItem(filePath);
                        }
                    }
                    catch
                    {
                        // could not add file
                    }

                    ScanVideoFolder(directoryPath);
                }

                try
                {
                    foreach (string filePath in Directory.GetFiles(path))
                    {
                        if (MediaTypes.IsValidVideoMediaType(filePath))
                            videoMediaCollection.AddItem(filePath);
                    }
                }
                catch
                {
                    // could not add file
                }
            }
            catch
            {
                // could not find path
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        private void WatchFolderPlaylist()
        {
            string path1 = Settings.PlaylistFolderMusic;
            string path2 = Settings.PlaylistFolderVideo;

            PlaylistCollection pc = PlaylistCollection.GetPlaylistCollection();
            Collection<Playlist> playlistToRemove = new Collection<Playlist>();

            while (true)
            {
                try
                {
                    playlistMediaCollection.ClearItems();

                    if (path1 != null)
                    {
                        // scan the first music folder
                        ScanPlaylistFolder(path1);

                        this.CurrentPlaylistFolder = "Currently adding files to the library.";

                        foreach (Playlist item in playlistMediaCollection)
                        {
                            while (this.PausePlaylist)
                                ;

                            var isInCollection = (from media in pc where media.Path == item.Path select media);

                            if (isInCollection.Count() == 0 && ExcludedMediaCollection.GetExcludedMediaCollection().IsExcluded(item.Path) == false)
                                pc.AddItem(item);
                        }

                        playlistMediaCollection.ClearItems();

                        _PlaylistScanComplete(EventArgs.Empty);
                    }

                    if (path2 != null)
                    {
                        // scan the first music folder
                        ScanPlaylistFolder(path2);

                        this.CurrentPlaylistFolder = "Currently adding files to the library.";

                        foreach (Playlist item in playlistMediaCollection)
                        {
                            var isInCollection = (from media in pc where media.Path == item.Path select media);

                            if (isInCollection.Count() == 0 && ExcludedMediaCollection.GetExcludedMediaCollection().IsExcluded(item.Path) == false)
                                pc.AddItem(item);
                        }

                        playlistMediaCollection.ClearItems();

                        _PlaylistScanComplete(EventArgs.Empty);
                    }

                    // remove any media that no longer exists
                    foreach (Playlist item in pc)
                    {
                        if (!File.Exists(item.Path))
                            playlistToRemove.AddItem(item);
                    }

                    foreach (Playlist item in playlistToRemove)
                        pc.RemoveItem(item);

                    _PlaylistScanComplete(EventArgs.Empty);

                    this.CurrentPlaylistFolder = "Scan Complete! Next Scan " + DateTime.Now.AddMinutes(Settings.WatchFolderScanInterval).ToLongTimeString();

                    // wait for next scan
                    Thread.Sleep(Settings.WatchFolderScanInterval * 60000);
                }
                catch
                {
                }
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        private void ScanPlaylistFolder(string path)
        {
            if (FileSystemAccess.IsRestricted(path))
                return;

            this.CurrentPlaylistFolder = path;

            try
            {
                foreach (string directoryPath in Directory.GetDirectories(path))
                {
                    try
                    {
                        foreach (string filePath in Directory.GetFiles(directoryPath))
                        {
                            if (MediaTypes.IsValidPlaylistMediaType(filePath))
                                playlistMediaCollection.AddItem(new Playlist() { Path = filePath, Title = Path.GetFileNameWithoutExtension(filePath) });
                        }
                    }
                    catch
                    {
                        // could not add file
                    }

                    ScanPlaylistFolder(directoryPath);
                }

                try
                {
                    foreach (string filePath in Directory.GetFiles(path))
                    {
                        if (MediaTypes.IsValidPlaylistMediaType(filePath))
                            playlistMediaCollection.AddItem(new Playlist() { Path = filePath, Title = Path.GetFileNameWithoutExtension(filePath) });
                    }
                }
                catch
                {
                    // could not add file
                }
            }
            catch
            {
                // could not find path
            }
        }
    }
}
